import { _decorator, Component, Node, SkeletalAnimation, RigidBody, Collider, CCFloat, Vec3, v3, ICollisionEvent, PhysicsSystem } from 'cc';
import { Events } from '../events/Events';
import { MathUtil } from '../utils/MathUtil';
import { ActorProperty } from './ActorProperty';
import { Projectile } from './Projectile';
import { EState } from './StateDefine';
const { ccclass, property } = _decorator;

@ccclass('Actor')
export class Actor extends Component {

    private curState: EState = EState.Idle
    private linearVelocity: Vec3 = new Vec3()
    private angularVelocity: Vec3 = new Vec3()

    private rigidbody: RigidBody = null
    private collider: Collider = null

    @property(SkeletalAnimation)
    public skeletalAnimation: SkeletalAnimation = null

    @property(CCFloat)
    private moveSpeed: number = 1
    @property(CCFloat)
    private rotateSpeed: number = 20
    private attackInterval: number = 0

    public input: Vec3 = new Vec3() //遥感的输入值
    public actorProp: ActorProperty = new ActorProperty()

    start() {
        this.rigidbody = this.node.getComponent(RigidBody)
        this.collider = this.node.getComponent(Collider)
        this.collider.on("onTriggerEnter", this.onTriggerEnter, this)
    }

    update(deltaTime: number) {
        this.attackInterval += deltaTime
        switch (this.curState) {
            case EState.Run:
                this.onMove()
                this.onRotate()
                break
            case EState.Attack:
                this.onRotate()
                if (this.attackInterval > 0.3) {
                    this.onAttack()
                    this.attackInterval = 0
                }
                break
            default:
                this.onStop()
        }
    }
    changState(newState: EState) {
        //如果和当前状态是一样的，就不用切换
        if (this.curState === newState) return
        //死亡状态下，不能切换状态
        if (this.curState === EState.Die) return
        //收到攻击的状态下，只能切换到死亡状态
        if (this.curState === EState.Hit && newState != EState.Die) return
        //只要新状态不是移动，就要停止
        if (newState !== EState.Run) this.onStop()

        this.curState = newState
        this.changeAnimation()
    }

    private changeAnimation() {
        this.skeletalAnimation.crossFade(this.curState, 0.2) //带有过渡效果的动画播放
    }
    private reSpawn() {
        this.changState(EState.Idle)
    }
    private onAttack() {
        this.skeletalAnimation.crossFade(this.curState, 0.3)
    }
    private onMove() {
        // 每一帧都调用的，所以linearVelocity最好一开始就初始化,否则对gc是个挑战
        const linerarSpeed = this.input.length() * this.moveSpeed
        this.linearVelocity.x = this.node.forward.x * linerarSpeed
        this.linearVelocity.z = this.node.forward.z * linerarSpeed
        this.linearVelocity.y = 0
        this.rigidbody.setLinearVelocity(this.linearVelocity) //这个方法在编辑器里调试无用，坑了我好久
    }
    private onRotate() {
        const angle = MathUtil.signAngle(this.node.forward, this.input, Vec3.UP)
        this.angularVelocity.x = 0
        this.angularVelocity.z = 0
        this.angularVelocity.y = angle * this.rotateSpeed
        this.rigidbody.setAngularVelocity(this.angularVelocity) //绕y轴旋转
    }
    private onStop() {
        this.rigidbody.setLinearVelocity(Vec3.ZERO)
    }
    private onDie() {
        this.changState(EState.Die)
        this.node.emit(Events.OnDie, this.node)
    }
    private onTriggerEnter(event: ICollisionEvent) {
        if (event.otherCollider.getGroup() == PhysicsSystem.PhysicsGroup.DEFAULT) return
        const projectile = event.otherCollider.getComponent(Projectile)
        const hostActor = projectile.host.getComponent(Actor)

        let hurtDirection = new Vec3()
        Vec3.subtract(hurtDirection, this.node.worldPosition, projectile.node.worldPosition)
        this.hurt(hostActor.actorProp.attack, hurtDirection, projectile.host)
    }

    hurt(damage: number, hurtDirection: Vec3, hurtSource: Node) {
        if (this.curState == EState.Die) return
        this.actorProp.hp -= damage
        this.node.emit(Events.OnHurt, this.actorProp.hp / this.actorProp.maxHp)
        if (this.actorProp.hp <= 0) {
            this.onDie()
            hurtSource.emit(Events.OnKill, this)
        } else {
            this.changState(EState.Hit)
        }
        //给角色造成冲量
        hurtDirection.multiplyScalar(2)
        this.rigidbody.applyImpulse(hurtDirection)
    }
}


